package handler

import (
	"GateWay/api/tool"
	"GateWay/model/request"
	"GateWay/model/response"
	"GateWay/rpc/pb"
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/status"
)

func (g *Gateway) JWTAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 1.获取请求中的Access Token和Refresh Token
		accessToken := tool.GetAccessToken(c)
		refreshToken := tool.GetRefreshToken(c)

		// 连接rpc服务端
		// 2.连接jwt的rpc和user客户端
		var ClientJwt pb.JwtClient
		var ClientUser pb.UserClient
		conn, _ := grpc.NewClient(
			tool.Addr(),
			grpc.WithTransportCredentials(insecure.NewCredentials()),
		)
		ClientJwt = pb.NewJwtClient(conn)
		ClientUser = pb.NewUserClient(conn)
		// 最后关闭这个连接
		defer conn.Close()

		// 3.检查这个refresh token是不是在黑名单中
		// 检查Refresh Token是否在黑名单中，如果是，则清除Refresh Token并返回未授权错误
		BlackReq := &pb.RefreshReq{
			RefreshToken: refreshToken,
		}

		// 判断一下是不是在黑名单里面
		_, err := ClientJwt.IsInBlackList(c, BlackReq)
		st, ok := status.FromError(err)
		// 判断是服务器问题还是黑名单存在
		if ok {
			// 如果是服务器问题
			switch st.Code() {
			case codes.Internal:
				response.FailWithMsg(c, "内部服务器问题")
				c.Abort() // 终止请求的后续处理
				return
			case codes.NotFound:
				break
			case codes.OK:
				tool.ClearRefreshToken(c)
				response.NoAuth(c, "Account logged in from another location or token is invalid")
				c.Abort() // 终止请求的后续处理
			}
		}

		// 创建一个JWT实例，用于后续的token解析与验证
		j := tool.NewJWT()

		// 解析Access Token
		claims, err := j.ParseAccessToken(accessToken)
		if err != nil {
			// 如果解析失败并且Access Token为空或过期
			if accessToken == "" || errors.Is(err, tool.TokenExpired) {
				// 尝试解析Refresh Token
				_, err := j.ParseRefreshToken(refreshToken)
				if err != nil {
					// 如果Refresh Token也无法解析，清除Refresh Token并返回未授权错误
					tool.ClearRefreshToken(c)
					response.NoAuth(c, "Refresh token expired or invalid")
					c.Abort()
					return
				}

				// 如果Refresh Token有效，通过其UserID获取用户信息
				GetUserReq := &pb.GetUserReq{
					Uuid: tool.GetUUID(c),
				}
				result, err := ClientUser.GetUser(c, GetUserReq)
				if err != nil {
					st, ok := status.FromError(err)
					if ok {
						// 如果是服务器问题
						switch st.Code() {
						case codes.Internal:
							response.FailWithMsg(c, "内部服务器问题")
							c.Abort() // 终止请求的后续处理
							return
						case codes.NotFound:
							// 如果没有找到该用户，清除Refresh Token并返回未授权错误
							tool.ClearRefreshToken(c)
							response.NoAuth(c, "The user does not exist")
							c.Abort()
							return
						}
					}
					// 如果黑名单存在
					tool.ClearRefreshToken(c)
					response.NoAuth(c, "Account logged in from another location or token is invalid")
					c.Abort() // 终止请求的后续处理
					return
				}

				// 使用Refresh Token的用户信息创建一个新的Access Token的Claims
				UUID, _ := uuid.Parse(result.Uuid)
				newAccessClaims := request.JwtAccessClaims{
					UserID: uint(result.Id),
					UUID:   UUID,
				}
				// 创建新的Access Token
				newAccessToken, err := j.CreateAccessToken(newAccessClaims)
				if err != nil {
					// 如果生成新的Access Token失败，清除Refresh Token并返回未授权错误
					tool.ClearRefreshToken(c)
					response.NoAuth(c, "Failed to create new access token")
					c.Abort()
					return
				}

				// 将新的Access Token和过期时间添加到响应头中
				c.Header("new-access-token", newAccessToken)
				//c.Header("new-access-expires-at", strconv.FormatInt(newAccessClaims.ExpiresAt.Unix(), 10))

				// 将新的Claims信息存入Context，供后续使用
				c.Set("claims", &newAccessClaims)
				c.Next() // 继续后续的处理
				return
			}

			// 如果Access Token无效且不满足刷新条件，清除Refresh Token并返回未授权错误
			tool.ClearRefreshToken(c)
			response.NoAuth(c, "Invalid access token")
			c.Abort()
			return
		}

		// 如果Access Token合法，将其Claims信息存入Context
		c.Set("claims", claims)
		// 需要通过网络请求的请求它把内容传出去，写一个中间件接受，存入对应的上下文中去

		c.Next() // 继续后续的处理
	}
}
